-
Notifications
You must be signed in to change notification settings - Fork 0
Implement full-stack TypeScript Hello World demo with Express + Vite #2
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Conversation
Co-authored-by: svg153 <[email protected]>
Co-authored-by: svg153 <[email protected]>
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pull Request Overview
This PR implements a full-stack TypeScript Hello World demo application with Express backend and Vite frontend, demonstrating modern development practices and tooling setup.
Key changes:
- Complete backend setup with Express server, TypeScript, ESM modules, and health/hello API endpoints
- Frontend application using Vite + TypeScript with API integration and responsive UI design
- Comprehensive development infrastructure including Vitest testing, ESLint/Prettier, GitHub Actions CI, and Docker support
Reviewed Changes
Copilot reviewed 18 out of 21 changed files in this pull request and generated 8 comments.
Show a summary per file
| File | Description |
|---|---|
src/server.ts |
Express server with API endpoints, static file serving, and environment configuration |
src/__tests__/server.test.ts |
Backend API tests for hello and health endpoints using Supertest |
frontend/src/main.ts |
Frontend application logic with API fetching and DOM manipulation |
frontend/src/utils.ts |
Utility function for message formatting |
frontend/src/__tests__/main.test.ts |
Frontend unit tests for utility functions |
frontend/index.html |
HTML template with embedded styles and responsive gradient design |
frontend/tsconfig.json |
Frontend-specific TypeScript configuration for browser environment |
package.json |
Project dependencies, scripts, and metadata |
tsconfig.json |
Backend TypeScript configuration with strict mode and ESM output |
vite.config.ts |
Vite configuration with dev server proxy and build settings |
vitest.config.ts |
Test framework configuration with coverage reporting |
.eslintrc.json |
ESLint configuration for TypeScript with recommended rules |
.prettierrc.json |
Code formatting rules |
.env.example |
Environment variable template |
.gitignore |
Git ignore patterns for dependencies, build output, and IDE files |
.dockerignore |
Docker ignore patterns to reduce build context |
Dockerfile |
Multi-stage Docker build for backend and frontend |
docker-compose.yml |
Docker Compose configuration with health checks |
.github/workflows/ci.yml |
GitHub Actions CI workflow with Node.js matrix testing |
README.md |
Comprehensive documentation with setup instructions and architecture details |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| coverage | ||
| .git | ||
| .gitignore | ||
| README.md |
Copilot
AI
Nov 17, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
[nitpick] The .dockerignore file excludes README.md on line 10 and then uses a broader pattern *.md on line 13, which also excludes README.md. The first specific exclusion is redundant. While not harmful, it's cleaner to remove line 10 since line 13 already covers it.
| README.md |
| "@types/supertest": "^6.0.2", | ||
| "@typescript-eslint/eslint-plugin": "^6.15.0", | ||
| "@typescript-eslint/parser": "^6.15.0", | ||
| "@vitejs/plugin-react": "^4.2.1", |
Copilot
AI
Nov 17, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The package.json includes "@vitejs/plugin-react": "^4.2.1" in devDependencies, but this plugin is not used anywhere in the codebase. The project uses vanilla TypeScript without React. This dependency should be removed to reduce bundle size and avoid confusion.
| "@vitejs/plugin-react": "^4.2.1", |
| if: matrix.node-version == '20.x' | ||
| uses: codecov/codecov-action@v3 | ||
| with: | ||
| file: ./coverage/coverage-final.json |
Copilot
AI
Nov 17, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In the CI workflow, the coverage upload step references ./coverage/coverage-final.json but Vitest with the v8 provider generates coverage in different file formats. The standard output is typically in subdirectories like ./coverage/lcov.info or the JSON files might have different names. This could cause the coverage upload to fail silently. Consider using a glob pattern or verifying the actual output path from Vitest's v8 coverage provider.
| file: ./coverage/coverage-final.json | |
| file: ./coverage/*.json,./coverage/lcov.info |
| // Start server | ||
| app.listen(PORT, () => { | ||
| console.log(`Server running on http://localhost:${PORT}`); | ||
| console.log(`API available at http://localhost:${PORT}/api/hello`); | ||
| }); |
Copilot
AI
Nov 17, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The server exports the Express app and immediately calls app.listen(). This creates a problem: when the tests import the server module to test the endpoints, the server will start listening, which can cause port conflicts and makes tests harder to run in parallel. Consider separating the app definition from the server startup. A common pattern is to export the app without starting it, and have a separate entry point (or check for import.meta.url === process.argv[1]) that starts the server only when run directly.
| // Start server | |
| app.listen(PORT, () => { | |
| console.log(`Server running on http://localhost:${PORT}`); | |
| console.log(`API available at http://localhost:${PORT}/api/hello`); | |
| }); | |
| // Start server only if this file is run directly | |
| if (import.meta.url === `file://${process.argv[1]}`) { | |
| app.listen(PORT, () => { | |
| console.log(`Server running on http://localhost:${PORT}`); | |
| console.log(`API available at http://localhost:${PORT}/api/hello`); | |
| }); | |
| } |
| COPY src ./src | ||
|
|
||
| # Build backend | ||
| RUN npm run build |
Copilot
AI
Nov 17, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The Dockerfile runs npm run build on line 17, but at that point only the backend source files have been copied (line 14). The frontend source files are not available yet, so npm run build which includes npm run frontend:build will fail. The backend and frontend builders should be independent, with the backend builder only running tsc to compile TypeScript.
| RUN npm run build | |
| RUN npx tsc |
| - NODE_ENV=production | ||
| - PORT=3000 | ||
| healthcheck: | ||
| test: ["CMD", "wget", "--quiet", "--tries=1", "--spider", "http://localhost:3000/api/health"] |
Copilot
AI
Nov 17, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The health check uses wget but the base image is node:20-alpine which doesn't include wget by default. The health check will fail. Consider using curl (which is available in alpine) or install wget in the Dockerfile, or use a Node.js-based health check command like node -e "require('http').get('http://localhost:3000/api/health')" or wget should be installed via apk add --no-cache wget.
| test: ["CMD", "wget", "--quiet", "--tries=1", "--spider", "http://localhost:3000/api/health"] | |
| test: ["CMD", "curl", "--fail", "http://localhost:3000/api/health"] |
| volumes: | ||
| node_modules: |
Copilot
AI
Nov 17, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The volumes section at the end defines a node_modules volume but it's never used by any service. This volume declaration serves no purpose and should either be removed or actually mounted to the backend service if the intention was to persist node_modules.
| volumes: | |
| node_modules: |
| # Create .env file with default values if not exists | ||
| RUN echo "PORT=3000" > .env.example | ||
|
|
Copilot
AI
Nov 17, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Creating a .env.example file inside the Docker image is misleading since this is runtime, not a template for users. The comment says "Create .env file with default values if not exists" but the command creates .env.example, not .env. If the intention is to provide default environment values, they should either be set as ENV variables directly or this line should be removed entirely since environment variables are already set via docker-compose.yml.
| # Create .env file with default values if not exists | |
| RUN echo "PORT=3000" > .env.example |
MVP full-stack application demonstrating modern TypeScript development setup with Express backend and Vite frontend.
Architecture
Backend (
src/server.ts)/api/hello→{ message: "Hello World" }/api/health→ health check endpointFrontend (
frontend/)/api/helloon load with error handlingTesting & Quality
Docker
Multi-stage build:
Run:
docker-compose up→ http://localhost:3000Scripts
npm run devnpm run frontend:devnpm run buildnpm startnpm testScreenshot
Minimal friction setup for rapid full-stack TypeScript demos.
Original prompt
Automated by Copilot MCP Server
✨ Let Copilot coding agent set things up for you — coding agent works faster and does higher quality work when set up for your repo.